import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * macChannel:访问介质,即传输信道
 * 传输Frame;
 * 需要被Station监听状态(是否正在传输Frame,isTransmitting);
 * 当一个时间槽同时多个Station传输Frame时,发生碰撞;
 * 若在时间槽开始时,有一个Station通需要发送,则能发送成功
 * 在仿真中,控制Frame传输成功时通知Source Station,即ACK;
 */

public class MacChannel {
    //正在传输的Frame
    private Frame transmittingFrame;
    //是否正在传输,用于载波监听
    private Boolean isTransmitting;
    //剩余传输时间
    private int remainTransmitTime;
    ////当前时间槽需要传输的Station的站的数量
    //private int stationToTransmitNum;
    //接收到的帧列表
    private List<Frame> framesToTransmit;

    //在该信道上的所有Station的集合
    Map<Integer, Station> stationMap;

    {
        isTransmitting = false;
        framesToTransmit = new ArrayList<>();
        stationMap = new HashMap<>();
    }

    //信道是否空闲
    Boolean isTransmittable() {
        return !this.isTransmitting;
    }

    void addStation(Station station) {
        stationMap.put(station.getNo(), station);
    }

    //由Station放置一个Frame,尝试发送
    void putFrame(Frame frameToTransmit) {
        this.framesToTransmit.add(frameToTransmit);
        //this.stationToTransmitNum++;
        this.isTransmitting = true;
    }

    //根据Station发送的Frame的数量判断是否发生碰撞
    //前提:存在传输帧,若大于1,则有多个Station在传输;否则,没有传输或只有一个
    private Boolean detectCollision() {
        return this.framesToTransmit.size() > 1;
    }


    //模拟发生ACK帧过程,告知Station传输成功
    void sendACK() {
        transmittingFrame.setSendSuccessTime(TimeLine.current);
        transmittingFrame.sourceStation.sendSuccessfully();
    }

    //模拟碰撞时帧发送超时
    void sendTimeout() {
        //对所有发送帧的站返回失败消息
        for (Frame frame : framesToTransmit) {
            frame.sourceStation.sendFail();
        }
    }

    //完成传输过程,若无传输帧,什么都不做
    //在每个时间槽开始时调用,检查该时间槽是否需要进行传输工作
    //若需要,返回true,并进行传输(time--)
    //若不需要传输,返回false,并准备接收新的帧
    //若有传输帧
    Boolean doTransmission() {
        if (!isTransmitting) {
            //若没有在传输帧
            return false;
        } else {
            //若正在传输帧.传输时间--
            remainTransmitTime--;
        }
        //检测是否传输结束
        if (remainTransmitTime == 0) {//传输结束
            //根据是否碰撞决定是否发送消息
            if (detectCollision()) {
                this.sendTimeout();
            } else {
                this.sendACK();
            }
            setTransmissionOver();
        }
        //只要是在传输帧,则本时间槽不可用
        return true;
    }

    void setTransmissionOver() {
        //传输(或碰撞)过程结束后,清空当前传输帧列表
        this.framesToTransmit.clear();
        this.transmittingFrame = null;
        this.isTransmitting = false;
    }

    //所有站都发送完帧了,开始进行传输
    //根据是否碰撞,该过程所需的时间槽数量不同
    void startTransmission() {
        if (framesToTransmit.isEmpty()) {
            Recorder.emptyNum ++;
//            System.out.println("empty:"+TimeLine.current);
            //接收到帧列表为空,则本时间槽没有Station发送帧
        } else {
            //若有至少一个Station发送帧
            Recorder.sendFrameNumOfChannel++;
            this.isTransmitting = true;
            if (detectCollision()) {//若发生碰撞
                remainTransmitTime = TimeLine.TRANSMISSION_TIMEOUT;
                Recorder.collisionNumOfChannel++;
            } else {//若不发生碰撞
                remainTransmitTime = TimeLine.TRANSMISSION_DURATION;
                this.transmittingFrame = framesToTransmit.get(0);
            }
        }
    }
}
